package com.gnerv.sylvanas.authentication.security.autoconfigure;

import com.gnerv.sylvanas.authentication.security.model.AccountDetails;
import com.gnerv.sylvanas.authentication.security.service.UriAuthorityService;
import com.gnerv.sylvanas.business.manage.model.dto.AuthAccountDTO;
import com.gnerv.sylvanas.business.manage.storage.AccountStorageService;
import com.gnerv.sylvanas.business.manage.storage.ApiStorageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;


/**
 * @author Gnerv LiGen
 */
@Slf4j
@AutoConfiguration
@ConditionalOnClass(name = {"com.gnerv.sylvanas.business.manage.storage.ApiStorageService", "com.gnerv.sylvanas.business.manage.storage.AccountStorageService", "com.gnerv.sylvanas.business.manage.storage.ApiStorageService"})
@EnableConfigurationProperties(SylvanasSecurityConfigProperties.class)
@AutoConfigureBefore(SylvanasSecurityAutoConfiguration.class)
public class SylvanasSecurityManageAutoConfiguration {

    private final SylvanasSecurityConfigProperties sylvanasSecurityConfigProperties;

    private static InMemoryUserDetailsManager inMemoryUserDetailsManager;

    public SylvanasSecurityManageAutoConfiguration(SylvanasSecurityConfigProperties sylvanasSecurityConfigProperties1) {
        this.sylvanasSecurityConfigProperties = sylvanasSecurityConfigProperties1;
        User.UserBuilder users = User.withDefaultPasswordEncoder();
        UserDetails superadmin = users
                .username(sylvanasSecurityConfigProperties.getSuperAdmin())
                .password("password")
                .roles("SUPERADMIN")
                .authorities(Collections.singletonList(new SimpleGrantedAuthority("ROLE_SUPERADMIN")))
                .build();
        inMemoryUserDetailsManager = new InMemoryUserDetailsManager(superadmin);
    }


    @Bean
    public UriAuthorityService manageUriAuthorityService(ApiStorageService apiStorageService) {
        return new UriAuthorityService() {

            @Override
            public String selectAuthority(String uri, String type) {
                return apiStorageService.selectAuthorityByUriAndType(uri, type);
            }

            @Override
            public boolean verify(Collection<? extends GrantedAuthority> authorities, String authority) {
                if (authority == null || authority.isEmpty()) {
                    return true;
                }
                List<String> list = authorities.stream().map(GrantedAuthority::getAuthority).toList();
                return list.contains(authority);
            }
        };
    }


    @Bean
    public UserDetailsService manageUserDetailsService(AccountStorageService accountStorageService, ApiStorageService apiStorageService) {
        return username -> {
            if (sylvanasSecurityConfigProperties.getSuperAdmin().equals(username)) {
                return inMemoryUserDetailsManager.loadUserByUsername(username);
            } else {
                AuthAccountDTO authAccountDTO = accountStorageService.selectAuthAccountByUsername(username);
                Set<String> authorityList = apiStorageService.selectListAuthorityByAccountUkId(authAccountDTO.getUkId());
                AccountDetails accountDetails = new AccountDetails();
                accountDetails.setUkId(authAccountDTO.getUkId());
                accountDetails.setUsername(authAccountDTO.getUsername());
                accountDetails.setPassword(authAccountDTO.getPassword());
                accountDetails.setStatus(authAccountDTO.getStatus());
                accountDetails.setAuthoritiesList(authorityList.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toSet()));
                return accountDetails;
            }
        };
    }

}
